/*
 * Copyright (C) 2004 cologic, blackclaw@parsoma.net
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#if !defined(AFX_SCRIPTMANAGER_H__730F7CF5_5C7D_4A2A_827B_53267D0EF4C5__INCLUDED_)
#define AFX_SCRIPTMANAGER_H__730F7CF5_5C7D_4A2A_827B_53267D0EF4C5__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "DCPlusPlus.h"
#include "Singleton.h"
#include "User.h"
#include "Socket.h"
#include "TimerManager.h"
#include "ClientManagerListener.h"
#include "CriticalSection.h"

#include <lunar.h>

class ScriptManagerListener {
public:
	typedef ScriptManagerListener* Ptr;
	typedef vector<Ptr> List;
	typedef List::iterator Iter;
	
	enum Types {
		DEBUG_MESSAGE,
	};

	virtual void on(Types, const string&) throw() = 0;
};

struct LuaManager {
	static const char className[];
	static Lunar<LuaManager>::RegType methods[];

	LuaManager(lua_State* /* L */) { }
	int SendClientMessage(lua_State* L);
	int SendHubMessage(lua_State* L);
	int GenerateDebugMessage(lua_State* L);
	int GetHubIpPort(lua_State* L);
	int GetClientIp(lua_State* L);
	int SendUDPPacket(lua_State* L);
	int InjectHubMessageNMDC(lua_State* L);
	int InjectHubMessageADC(lua_State* L);
	int HubWindowAttention(lua_State* L);
	int FindWindow(lua_State* L);
	int PostMessage(lua_State* L);

	int CreateClient(lua_State* L);
	int DeleteClient(lua_State* L);

	int SocketAccept(lua_State* L);
	int SocketConnect(lua_State* L);
	int SocketWrite(lua_State* L);
	int SocketClose(lua_State* L);

	int GetSetting(lua_State* L);
};

class ScriptInstance {
	bool MakeCallRaw(const string& table, const string& method, int args, int ret) throw();
protected:
	virtual ~ScriptInstance() { }
	static lua_State* L;
	static CriticalSection cs;

	template <typename T> bool MakeCall(const string& table, const string& method,
		int ret, const T& t) throw() {
		Lock l(cs);
		dcassert(lua_gettop(L) == 0);
		LuaPush(t);
		return MakeCallRaw(table, method, 1, ret);
	}
	template <typename T, typename T2> bool MakeCall(const string& table, const string& method,
		int ret, const T& t, const T2& t2) throw() {
		Lock l(cs);
		dcassert(lua_gettop(L) == 0);
		LuaPush(t);
		LuaPush(t2);
		return MakeCallRaw(table, method, 2, ret);
	}
	template <typename T> void LuaPush(T* p) { lua_pushlightuserdata(L, p); }
	void LuaPush(int i);
	void LuaPush(const string& s);
	bool GetLuaBool();
	//string GetClientType(Client* aClient);
public:
	void EvaluateFile(const string& fn);
	void EvaluateChunk(const string& chunk);
};

class SocketPool : private Thread, public ScriptInstance {
	static const int RECV_BUFFER_SIZE = 2048, WM_ASYNCDNS = WM_USER + 1;
	int initialized;
	HANDLE iocp;
	HWND wndDNS;
	char readBuf[RECV_BUFFER_SIZE];
	DWORD dwBytes;

	struct OVERLAPPEDPLUS {
		OVERLAPPED ol;
		SOCKET s, sClient;
		int OpCode;
		char lpOutputBuf[2*(sizeof(sockaddr_in)+16)];

		int cookie;
		int len;
		AutoArray<char> varSendBuf;
		char *sendBuf;

		OVERLAPPEDPLUS(int opCode, SOCKET s, SOCKET sClient, int len = 0) : varSendBuf(len) {
			memset(&ol, 0, sizeof(ol));
			OpCode = opCode;
			this->s = s;
			this->sClient = sClient;
			sendBuf = (char *)varSendBuf;
		}
	};

	//Damn, all this just for DNS lookups...
	struct ConnectBuffer {
		ConnectBuffer(SocketPool *_context = NULL, short _port = 0, int _cookie = -1)
			: context(_context), port(_port), hostbuf(new char[MAXGETHOSTSTRUCT]), cookie(_cookie) { }
		ConnectBuffer(const ConnectBuffer& cb)
			: context(cb.context), port(cb.port), hostbuf(cb.hostbuf), cookie(cb.cookie) {
			const_cast<ConnectBuffer &>(cb).hostbuf = 0;
		}
		ConnectBuffer& operator=(const ConnectBuffer &cb) {
			context = cb.context;
			port = cb.port;
			cookie = cb.cookie;
			delete hostbuf;
			hostbuf = cb.hostbuf;
			const_cast<ConnectBuffer &>(cb).hostbuf = 0;
			return *this;
		}
		SocketPool *context;
		short port;
		char *hostbuf;
		int cookie;
		~ConnectBuffer() { delete hostbuf; }
	};

	static CriticalSection cs;
	typedef map<HANDLE, ConnectBuffer> ConnectBuffers;
	static ConnectBuffers connectBuffers;

	enum {OP_READ, OP_WRITE, OP_ACCEPT, OP_CONNECT, OP_CLOSE};
	//not called by user; instead, eventloop calls back out.
	SOCKET AddSocket(SOCKET s);
	int Read(SOCKET s);

	int run();		//main socket threading
	//sigh. Win32 is bothersome.
	static LRESULT CALLBACK DNSWorker(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

	void OnAccept(SOCKET s, int cookie) { OnLuaCookieCall("OnAccept", s, cookie); }
	void OnConnect(SOCKET s, int cookie) { OnLuaCookieCall("OnConnect", s, cookie); }
	void OnRead(SOCKET s, char *buf, int len);
	void OnWrite(SOCKET s) { OnLuaSocketCall("OnWrite", s); }
	void OnClose(SOCKET s) { OnLuaSocketCall("OnClose", s); }
	void OnLuaSocketCall(const string &call, SOCKET s);
	void OnLuaCookieCall(const string &call, SOCKET s, int cookie);
	int Bind(SOCKET s, short port);
	void ConnectRaw(in_addr addr, short port, int cookie);
public:
	SocketPool();
	SOCKET NewSocket() { return AddSocket(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); }
	int Listen(SOCKET s, short port);
	int Accept(SOCKET s, int cookie);
	void Connect(std::string address, short port, int cookie);
	int Write(SOCKET s, const char *buf, int len);
	void Close(SOCKET s);		//should have a return value, but *shrug*
};

class ScriptManager : public ScriptInstance, public Singleton<ScriptManager>, public Speaker<ScriptManagerListener>,
		private ClientManagerListener, private TimerManagerListener
{
	Socket s;
	SocketPool ss;

	friend class Singleton<ScriptManager>;
	ScriptManager();
	virtual ~ScriptManager() { lua_close(L); }
public:
	void load();
	void SendDebugMessage(const string& s);
private:
	friend struct LuaManager;
	friend class ScriptInstance;


	//void on(DownloadManagerListener::Types type, Download* u) throw();
	void on(ClientManagerListener::Types, Client*) throw();
	virtual void on(Second, u_int32_t /* ticks */);
};

#endif // !defined(AFX_SCRIPTMANAGER_H__730F7CF5_5C7D_4A2A_827B_53267D0EF4C5__INCLUDED_)

/**
 * @file ScriptManager.h
 * $Id: ScriptManager.h 255 2005-07-16 13:06:57Z Flow84 $
 */